#include "myextend.h"

#include "logjam.h"

#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/avfiltergraph.h"
#include "libavutil/avutil.h"
#include "libavutil/dict.h"
#include "libavutil/fifo.h"
#include "libavutil/pixfmt.h"
#include "libavutil/rational.h"
#include "libswresample/swresample.h"

# include "libavfilter/avcodec.h"
# include "libavfilter/avfilter.h"
# include "libavfilter/avfiltergraph.h"
# include "libavfilter/buffersrc.h"
# include "libavfilter/buffersink.h"


/*release all resource and return errorCode*/
int exit_program(int errorCode){
	return 0;
}

int64_t parse_time_or_die(const char *context, const char *timestr, int is_duration)
{
     int64_t us;
     if (av_parse_time(&us, timestr, is_duration) < 0) {
         //fprintf(stderr, "Invalid %s specification for %s: %s\n", is_duration ? "duration" : "date", context, timestr);
         return -1;
     }
     return us;
}

int parse_forced_key_frames(char *kf, OutputStream *ost, AVCodecContext *avctx)
{
    char *p;
    int n = 1, i;
    int64_t t;

    for (p = kf; *p; p++)
        if (*p == ',')
            n++;
    ost->forced_kf_count = n;
    ost->forced_kf_pts = av_malloc(sizeof(*ost->forced_kf_pts) * n);
    if (!ost->forced_kf_pts) {
        //av_log(NULL, AV_LOG_FATAL, "Could not allocate forced key frames array.\n");
        return exit_program(-1);
    }
    for (i = 0; i < n; i++) {
        p = i ? strchr(p, ',') + 1 : kf;
        t = parse_time_or_die("force_key_frames", p, 1);
        if(t < 0)
        	return exit_program(-2);
        ost->forced_kf_pts[i] = av_rescale_q(t, AV_TIME_BASE_Q, avctx->time_base);
    }

    return 0;
}


int add_audio_stream(TCompressor *compressor, enum AVCodecID codec_id){

}

int add_video_stream(TCompressor *compressor, enum AVCodecID codec_id){

}

int write_frame(TCompressor *compressor, AVFormatContext *s, AVPacket *pkt, OutputStream *ost){
	AVBitStreamFilterContext *bsfc = ost->bitstream_filters;
	AVCodecContext          *avctx = ost->st->codec;
	int ret;

	if ((avctx->codec_type == AVMEDIA_TYPE_VIDEO && compressor->video_sync_method == VSYNC_DROP) ||
			(avctx->codec_type == AVMEDIA_TYPE_AUDIO && compressor->audio_sync_method < 0))
		pkt->pts = pkt->dts = AV_NOPTS_VALUE;

	if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && pkt->dts != AV_NOPTS_VALUE) {
		int64_t max = ost->st->cur_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT);
	    if (ost->st->cur_dts && ost->st->cur_dts != AV_NOPTS_VALUE &&  max > pkt->dts) {
	        pkt->pts = pkt->dts = max;
	    }
	}

	return 0;
}

int output_video(AVFormatContext *s, OutputStream *ost, AVFrame *in_picture, float quality){

}

int output_audio(AVFormatContext *s, OutputStream *ost, AVFrame *frame){

}

/*********************************************************************************************/

int doTranscode(char *inputFile, char *outputFile){
	TCompressor *compressor = (TCompressor*)malloc(sizeof(TCompressor));
	InputStream *ist;
	OutputStream *ost;
	AVCodecContext *oCodec = NULL;
	AVFormatContext *oc;
	int i;

	if(!compressor){
		return -1;
	}

	oc = compressor->outFmtCtx;

	//Tell the compressor when we start reading input
	for(i=0; i< compressor->nbStream; i++)
		compressor->inStreams[i]->start = av_gettime();

	/* output stream init */
	av_dump_format(compressor->outFmtCtx, i, compressor->outFmtCtx->filename, 1);

	/* for each output stream, we compute the right encoding parameters */
	for(i=0; i< compressor->nbStream; i++) {
		AVCodecContext *icodec = NULL;
		ost = compressor->outStreams[i];
		ist = compressor->inStreams[i];

		oCodec = ost->st->codec;

		icodec = ist->st->codec;
        ost->st->disposition          = ist->st->disposition;
        oCodec->bits_per_raw_sample    = icodec->bits_per_raw_sample;
        oCodec->chroma_sample_location = icodec->chroma_sample_location;

        if(ost->stream_copy){
        	uint64_t extra_size;
        	extra_size = (uint64_t)icodec->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE;
        	if (extra_size > INT_MAX) {
        		return AVERROR(EINVAL);
        	}

        	LOGI("[TRANSCODE_INIT] COME HERE 2.1 Codec_id=%d", icodec->codec_id);
        	/* if stream_copy is selected, no need to decode or encode */
        	oCodec->codec_id   = icodec->codec_id;
        	oCodec->codec_type = icodec->codec_type;

        	if (!oCodec->codec_tag) {
        		if (!oc->oformat->codec_tag ||
        				av_codec_get_id (oc->oformat->codec_tag, icodec->codec_tag) == oCodec->codec_id ||
        	            av_codec_get_tag(oc->oformat->codec_tag, icodec->codec_id) <= 0)
        			oCodec->codec_tag = icodec->codec_tag;
        	}

        	LOGI("[TRANSCODE_INIT] COME HERE 2.2");
        	oCodec->bit_rate       = icodec->bit_rate;
        	oCodec->rc_max_rate    = icodec->rc_max_rate;
        	oCodec->rc_buffer_size = icodec->rc_buffer_size;
        	oCodec->field_order    = icodec->field_order;
        	oCodec->extradata      = av_mallocz(extra_size);
        	if (!oCodec->extradata) {
        		return AVERROR(ENOMEM);
        	}

        	memcpy(oCodec->extradata, icodec->extradata, icodec->extradata_size);
        	oCodec->extradata_size= icodec->extradata_size;
        	oCodec->bits_per_coded_sample  = icodec->bits_per_coded_sample;

        	oCodec->time_base = ist->st->time_base;

        	/*
        	* Avi is a special case here because it supports variable fps but
        	* having the fps and timebase differe significantly adds quite some overhead */

        	if(!strcmp(oc->oformat->name, "avi")) {
        		if ( compressor->copy_tb<0 && av_q2d(ist->st->r_frame_rate) >= av_q2d(ist->st->avg_frame_rate)
        				&& 0.5/av_q2d(ist->st->r_frame_rate) > av_q2d(ist->st->time_base)
        	            && 0.5/av_q2d(ist->st->r_frame_rate) > av_q2d(icodec->time_base)
        	            && av_q2d(ist->st->time_base) < 1.0/500 && av_q2d(icodec->time_base) < 1.0/500 || compressor->copy_tb==2){
        	            	oCodec->time_base.num = ist->st->r_frame_rate.den;
        	                oCodec->time_base.den = 2*ist->st->r_frame_rate.num;
        	                oCodec->ticks_per_frame = 2;
        	    } else if ( compressor->copy_tb<0 && av_q2d(icodec->time_base)*icodec->ticks_per_frame > 2*av_q2d(ist->st->time_base)
        	                                 && av_q2d(ist->st->time_base) < 1.0/500 || compressor->copy_tb==0){
        	    	oCodec->time_base = icodec->time_base;
        	        oCodec->time_base.num *= icodec->ticks_per_frame;
        	        oCodec->time_base.den *= 2;
        	        oCodec->ticks_per_frame = 2;
        	    }
        	} else if(!(oc->oformat->flags & AVFMT_VARIABLE_FPS)
        				&& strcmp(oc->oformat->name, "mov") && strcmp(oc->oformat->name, "mp4") && strcmp(oc->oformat->name, "3gp")
        	            && strcmp(oc->oformat->name, "3g2")
        	            && strcmp(oc->oformat->name, "psp") && strcmp(oc->oformat->name, "ipod")) {
        	            	if( compressor->copy_tb<0 && av_q2d(icodec->time_base)*icodec->ticks_per_frame > av_q2d(ist->st->time_base)
        	                                && av_q2d(ist->st->time_base) < 1.0/500 || compressor->copy_tb==0){
        	            		oCodec->time_base = icodec->time_base;
        	                    oCodec->time_base.num *= icodec->ticks_per_frame;
        	                }
        	}

        	LOGI("[TRANSCODE_INIT] COME HERE 2.3");
        	if(ost->frame_rate.num)
        		oCodec->time_base = av_inv_q(ost->frame_rate);

        	av_reduce(&oCodec->time_base.num, &oCodec->time_base.den,
        	                        oCodec->time_base.num, oCodec->time_base.den, INT_MAX);
        	LOGI("[TRANSCODE_INIT] COME HERE 2.4");
        	switch (oCodec->codec_type) {
        		case AVMEDIA_TYPE_AUDIO:
        			if (compressor->audio_volume != 256) {
        				//av_log(NULL, AV_LOG_FATAL, "-acodec copy and -vol are incompatible (frames are not decoded)\n");
        	            return exit_program(-2);
        	        }
        	        oCodec->channel_layout     = icodec->channel_layout;
        	        oCodec->sample_rate        = icodec->sample_rate;
        	        oCodec->channels           = icodec->channels;
        	        oCodec->frame_size         = icodec->frame_size;
        	        oCodec->audio_service_type = icodec->audio_service_type;
        	        oCodec->block_align        = icodec->block_align;
        	        if((oCodec->block_align == 1 || oCodec->block_align == 1152) && oCodec->codec_id == AV_CODEC_ID_MP3)
        	        	oCodec->block_align= 0;
        	        if(oCodec->codec_id == AV_CODEC_ID_AC3)
        	        	oCodec->block_align= 0;
        	    break;
        	    case AVMEDIA_TYPE_VIDEO:
        	    	oCodec->pix_fmt            = icodec->pix_fmt;
        	        oCodec->width              = icodec->width;
        	        oCodec->height             = icodec->height;
        	        oCodec->has_b_frames       = icodec->has_b_frames;
        	        if (!oCodec->sample_aspect_ratio.num) {
        	        	oCodec->sample_aspect_ratio   =
        	                    ost->st->sample_aspect_ratio =
        	            ist->st->sample_aspect_ratio.num ? ist->st->sample_aspect_ratio :
        	            ist->st->codec->sample_aspect_ratio.num ?
        	            ist->st->codec->sample_aspect_ratio : (AVRational){0, 1};
        	        }

        	        ost->st->avg_frame_rate = ist->st->avg_frame_rate;
        	    break;
        	    case AVMEDIA_TYPE_SUBTITLE:
        	    	oCodec->width  = icodec->width;
        	        oCodec->height = icodec->height;
        	    break;
        	    case AVMEDIA_TYPE_DATA:
        	    case AVMEDIA_TYPE_ATTACHMENT:
        	    break;
        	    default:
        	    	abort();
        	}//end switch
        } //end if(stream_copy)
        else{
        	if(!ost->enc)
        		ost->enc = avcodec_find_encoder(oCodec->codec_id);

//        	if (!ost->filter && (codec->codec_type == AVMEDIA_TYPE_VIDEO ||
//        			codec->codec_type == AVMEDIA_TYPE_AUDIO)) {
//        	        FilterGraph *fg;
//        	        fg = init_simple_filtergraph(ist, ost);
//        	        if (configure_filtergraph(fg)) {
//        	        	//av_log(NULL, AV_LOG_FATAL, "Error opening filters!\n");
//        	            return exit_program(-3);
//        	        }
//        	}
        	if (oCodec->codec_type == AVMEDIA_TYPE_VIDEO) {
        		if (ost->filter && !ost->frame_rate.num)
        			ost->frame_rate = av_buffersink_get_frame_rate(ost->filter);
        	        if (ist && !ost->frame_rate.num)
        	        	ost->frame_rate = ist->st->r_frame_rate.num ? ist->st->r_frame_rate : (AVRational){25, 1};

        	        if (ost->enc && ost->enc->supported_framerates && !ost->force_fps) {
        	        	int idx = av_find_nearest_q_idx(ost->frame_rate, ost->enc->supported_framerates);
        	            ost->frame_rate = ost->enc->supported_framerates[idx];
        	        }
        	 }

        	switch(oCodec->codec_type){
        		case AVMEDIA_TYPE_AUDIO:
        			oCodec->sample_fmt     = ost->filter->inputs[0]->format;
        			oCodec->sample_rate    = ost->filter->inputs[0]->sample_rate;
        			oCodec->channel_layout = ost->filter->inputs[0]->channel_layout;
        			oCodec->channels       = av_get_channel_layout_nb_channels(oCodec->channel_layout);
        			oCodec->time_base      = (AVRational){ 1, oCodec->sample_rate };
        			break;
        		case AVMEDIA_TYPE_VIDEO:
        			oCodec->time_base = av_inv_q(ost->frame_rate);
        			if (ost->filter && !(oCodec->time_base.num && oCodec->time_base.den))
        				oCodec->time_base = ost->filter->inputs[0]->time_base;
        			if (   av_q2d(oCodec->time_base) < 0.001 && compressor->video_sync_method != VSYNC_PASSTHROUGH
        					&& (compressor->video_sync_method == VSYNC_CFR ||
        							(compressor->video_sync_method == VSYNC_AUTO && !(oc->oformat->flags & AVFMT_VARIABLE_FPS)))){
        				//av_log(oc, AV_LOG_WARNING, "Frame rate very high for a muxer not efficiently supporting it.\n"
        						//"Please consider specifying a lower framerate, a different muxer or -vsync 2\n");
        			}
        			for (i = 0; i < ost->forced_kf_count; i++)
        				ost->forced_kf_pts[i] = av_rescale_q(ost->forced_kf_pts[i], AV_TIME_BASE_Q, oCodec->time_base);
        			oCodec->width  = ost->filter->inputs[0]->w;
        			oCodec->height = ost->filter->inputs[0]->h;
        			oCodec->sample_aspect_ratio = ost->st->sample_aspect_ratio =
        			                    ost->frame_aspect_ratio ? // overridden by the -aspect cli option
        			                    av_d2q(ost->frame_aspect_ratio * oCodec->height/oCodec->width, 255) :
        			                    ost->filter->inputs[0]->sample_aspect_ratio;
        			oCodec->pix_fmt = ost->filter->inputs[0]->format;

        			if (!icodec || oCodec->width   != icodec->width  ||
        			                    oCodec->height  != icodec->height ||
        			                    oCodec->pix_fmt != icodec->pix_fmt) {
        			   	oCodec->bits_per_raw_sample = compressor->frame_bits_per_raw_sample;
        			}

        			if (ost->forced_keyframes)
        				parse_forced_key_frames(ost->forced_keyframes, ost, ost->st->codec);

        			break;
        	}//end switch encode_type

        }
	}//end for compute encoders

	//release resources
	free(compressor);
	return 0;
}
